﻿/* **********************************************************************************
 * Copyright (c) 2011 John Hughes
 *
 * Elve Touch is licenced under the Microsoft Reciprocal License (Ms-RL).
 *
 * Project Website: http://elvetouch.codeplex.com/
 * **********************************************************************************/

using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Imaging;

namespace ElveTouch
{
    public enum TouchServiceTcpCommunicationPayloadTypes : byte
    {
        Ping = 0,                   // used to test the network connection. No payload.
        Pong = 1,                   // just used as a response to the ping command. No Payload.
        Error = 2,
        Disconnect = 3,             // Tells the client or server to close the connection. No Payload.

        Hello = 4,                  // Client->Server: requests authentication token and specifies simple or full rendering, connection info like alpha channel support, native screen resolution and pixel bit depth.  Must be received within 30 seconds or the connection with drop.
        AuthenticateChallenge = 5,  // Server->Client: asking to authenticate
        Authenticate = 6,           // Client->Server: includes authentication info.
        AuthenticationResult = 7,   // Server->Client: indicates if authentication was successful.

        ShowMessage = 8,            // Server->Client: The client application should show a message.
        ChangeScreenMode = 9,       // Server->Client: The client app should change it's screen mode.
        Exit = 10,                  // Server->Client: The client app should close. (Usually from an Exit button press).

        TouchEvent = 11,             // Client->Server: the screen was touched, or the touch removed from the screen, or the touch was moved on the screen

        RequestRedraw = 12,          // Client->Server: requests that draw command be sent from the server for an area of the screen.
        RequestScreenSnapshot = 13, // Client->Server: requests a section of the current screen as an image. The server responds with a DrawImage command.
        RequestNamedImage = 14,     // Client->Server: requests a named image. The server responds with a NamedImage command.

        NamedImage = 15,            // Server->Client: This is the image data for the requested named image.

        StartDrawing = 16,          // Server->Client: Indicates that a section of the screen is starting to be drawn (use double buffering to avoid flicker). Includes the initial clipping area.
        EndDrawing = 17,            // Server->Client: Indicates that a section of the screen has completed drawing. The clipping stack should be cleared and the final double buffered image should be painted.
        ScreenChange = 18,          // Server->Client: Indicates that a section of the screen changed. This is only send for the Snapshot Rendering Mode.

        PushClippingArea = 19,      // Server->Client: Pushes a graphics clipping area (Set, Intersect, Exclude) onto the stack.
        PopClippingArea = 20,       // Server->Client: Pops the most recent clipping area off the stack.

        FillRectangle = 21,               // Server->Client: draw a solid color filled rectangle
        FillLinearGradientRectangle = 22, // Server->Client: draw a linear gradient filled rectangle
        DrawText = 23,                    // Server->Client: draw text
        DrawBorder = 24,                  // Server->Client: draw a border
        DrawImage = 25,                   // Server->Client: draw an image... payload includes the png image
        DrawNamedImage = 26,              // Server->Client: draw a named image
        DrawLine = 27,                    // Server->Client: draw a line

        LocationChangeEvent = 28,   // Server->Client: The viewer device's location changed.

        // TODO: need throttling commands and maybe a message command
    }

    public enum TouchServiceTcpCommunicationAuthenticationResults : byte
    {
        Success = 0,
        Invalid = 1,
        Disabled = 2,
        BlockedByTimeRestrictions = 3
    }

    public enum TouchClientAppID : byte
    {
        Unassigned = 0,
        WindowsMacLinuxPC = 1, // this is touch screen viewer lite running on windows, mac or linux.
        PocketPC = 2,
        IPhone = 3,
        Android = 4,
        WindowsPhone = 5
    }

    public enum RenderingMode : byte
    {
        Snapshots = 0, // Only prerendered snapshots are sent
        DrawCommands = 1 // Each draw command is sent.
    }

    public enum ShowMessageDisplayMode : byte
    {
        NonIntrusive = 0,
        RequiresAcknowledgment = 1
    }

    public enum ScreenMode : byte
    {
        FullScreen = 0,
        Windowed = 1,
        Minimized = 2,
        ToggleFullscreen = 3
    }

    public enum TouchEventType : byte
    {
        /// <summary>Occurs immediately when the screen is touched.</summary>
        TouchDown = 0,
        /// <summary>Occurs when the screen is touched and held for a 1/4 second, or when it is released (whichever occurs first).</summary>
        Touched = 1,
        /// <summary>Occurs when the user released their touch from the screen.</summary>
        TouchUp = 2,
        /// <summary>Occurs when the control is pressed twice in a short period of time.</summary>
        DoubleTouch = 3,
        /// <summary>The user has moved their finger across the screen.</summary>
        TouchMove = 4,
    }

    public enum PushClippingAreaMode : byte
    {
        Set = 0,
        Intersect = 1,
        Exclude = 2
    }

    public enum NamedImageFormat : byte
    {
        Png = 0,
        Gif = 1,
        Jpeg = 2,
        Bmp = 3,
        // The following are not supported on PocketPC and likely other devices.
        //Tiff,
        //Wmf,
        //Icon, 
        //Exif,
        //Emf
    }

    public struct ErrorPayload : IBinaryTcpPayload
    {
        public string Title;
        public string Message;
        public string ExceptionTypeName;
        public string Source;

        public ErrorPayload(byte[] data)
        {
            using (BinaryStreamReader sr = new BinaryStreamReader(data))
            {
                Title = sr.ReadString();
                Message = sr.ReadString();
                ExceptionTypeName = sr.ReadString();
                Source = sr.ReadString();
            }
        }

        public ErrorPayload(string title, string exceptionTypeName, string message, string source)
        {
            Title = title;
            Message = message;
            ExceptionTypeName = exceptionTypeName;
            Source = source;
        }

        public byte PayloadType
        {
            get { return (byte)TouchServiceTcpCommunicationPayloadTypes.Error; }
        }

        public byte[] ToByteArray()
        {
            using (BinaryStreamWriter s = new BinaryStreamWriter())
            {
                s.Write(Title);
                s.Write(Message);
                s.Write(ExceptionTypeName);
                s.Write(Source);

                return s.ToArray();
            }
        }
    }

    public struct HelloPayload : IBinaryTcpPayload
    {
        public byte ProtocolVersion; // should be 1 until a breaking change is made.
        public TouchClientAppID ApplicationID; // the ID of the client application.
        public RenderingMode RenderingMode;
        public Size ScreenSize;
        public int PixelDepthByteCount; // usually 24 or 32.
        public bool SupportsAlphaChannel;
        public string UniqueClientID; // The unique identifier of the device.  iphone should use UIDevice.UniqueIdentifier. This is used to help limit the # of touch screens.

        public HelloPayload(byte[] data)
        {
            using (BinaryStreamReader sr = new BinaryStreamReader(data))
            {
                ProtocolVersion = sr.ReadByte();
                ApplicationID = (TouchClientAppID)sr.ReadByte();
                RenderingMode = (RenderingMode)sr.ReadByte();
                ScreenSize = sr.ReadSize();
                PixelDepthByteCount = sr.ReadByte();
                SupportsAlphaChannel = sr.ReadBoolean();

                if (data.Length > 9)
                    UniqueClientID = sr.ReadString();
                else
                    UniqueClientID = "";
            }
        }

        public HelloPayload(byte protocolVersion, TouchClientAppID applicationID, RenderingMode renderingMode, Size screenSize, int pixelDepthByteCount, bool supportsAlphaChannel, string uniqueClientID)
        {
            ProtocolVersion = protocolVersion;
            ApplicationID = applicationID;
            RenderingMode = renderingMode;
            ScreenSize = screenSize;
            PixelDepthByteCount = pixelDepthByteCount;
            SupportsAlphaChannel = supportsAlphaChannel;
            UniqueClientID = uniqueClientID;
        }

        public byte PayloadType
        {
            get { return (byte)TouchServiceTcpCommunicationPayloadTypes.Hello; }
        }

        public byte[] ToByteArray()
        {
            using (BinaryStreamWriter s = new BinaryStreamWriter())
            {
                s.Write(ProtocolVersion);
                s.Write((byte)ApplicationID);
                s.Write((byte)RenderingMode);
                s.Write(ScreenSize);
                s.Write((byte)PixelDepthByteCount);
                s.Write(SupportsAlphaChannel);
                s.Write(UniqueClientID);

                return s.ToArray();
            }
        }
    }

    public struct AuthenticationChallengePayload : IBinaryTcpPayload
    {
        public byte[] AuthenticationToken;

        public AuthenticationChallengePayload(byte[] data)
        {
            using (BinaryStreamReader sr = new BinaryStreamReader(data))
            {
                AuthenticationToken = sr.ReadByteArrayWithLength();
            }
        }

        public AuthenticationChallengePayload(byte[] authenticationToken, bool junk)
        {
            AuthenticationToken = authenticationToken;
        }

        public byte PayloadType
        {
            get { return (byte)TouchServiceTcpCommunicationPayloadTypes.AuthenticateChallenge; }
        }

        public byte[] ToByteArray()
        {
            using (BinaryStreamWriter s = new BinaryStreamWriter())
            {
                s.WriteByteArrayWithLength(AuthenticationToken);

                return s.ToArray();
            }
        }
    }

    public struct AuthenticatePayload : IBinaryTcpPayload
    {
        public String Username;
        public byte[] Token; // Authentication Token from client in hex
        public byte[] Hash;  // resulting hash

        public AuthenticatePayload(byte[] data)
        {
            using (BinaryStreamReader sr = new BinaryStreamReader(data))
            {
                Username = sr.ReadString();
                Token = sr.ReadByteArrayWithLength();
                Hash = sr.ReadByteArrayWithLength();
            }
        }

        public AuthenticatePayload(string username, byte[] token, byte[] hash)
        {
            Username = username;
            Token = token;
            Hash = hash;
        }

        public byte PayloadType
        {
            get { return (byte)TouchServiceTcpCommunicationPayloadTypes.Authenticate; }
        }

        public byte[] ToByteArray()
        {
            using (BinaryStreamWriter s = new BinaryStreamWriter())
            {
                s.Write(Username);
                s.WriteByteArrayWithLength(Token);
                s.WriteByteArrayWithLength(Hash);

                return s.ToArray();
            }
        }
    }

    public struct AuthenticationResultPayload : IBinaryTcpPayload
    {
        public TouchServiceTcpCommunicationAuthenticationResults AuthenticationResult;
        public Size TouchScreenSize;
        public Color BackgroundColor;

        public AuthenticationResultPayload(byte[] data)
        {
            using (BinaryStreamReader sr = new BinaryStreamReader(data))
            {
                AuthenticationResult = (TouchServiceTcpCommunicationAuthenticationResults)sr.ReadByte();
                TouchScreenSize = sr.ReadSize();
                BackgroundColor = sr.ReadColor();
            }
        }

        public AuthenticationResultPayload(TouchServiceTcpCommunicationAuthenticationResults authenticationResult, Size touchScreenSize, Color backgroundColor)
        {
            AuthenticationResult = authenticationResult;
            TouchScreenSize = touchScreenSize;
            BackgroundColor = backgroundColor;
        }

        public byte PayloadType
        {
            get { return (byte)TouchServiceTcpCommunicationPayloadTypes.AuthenticationResult; }
        }

        public byte[] ToByteArray()
        {
            using (BinaryStreamWriter s = new BinaryStreamWriter())
            {
                s.Write((byte)AuthenticationResult);
                s.Write(TouchScreenSize);
                s.Write(BackgroundColor);

                return s.ToArray();
            }
        }
    }

    
    public struct RendererShowMessage : IBinaryTcpPayload
    {
        public ShowMessageDisplayMode DisplayMode;
        public string Text;

        public RendererShowMessage(byte[] data)
        {
            using (BinaryStreamReader sr = new BinaryStreamReader(data))
            {
                DisplayMode = (ShowMessageDisplayMode)sr.ReadByte();
                Text = sr.ReadString();
            }
        }

        public RendererShowMessage(ShowMessageDisplayMode displayMode, string text)
        {
            DisplayMode = displayMode;
            Text = text;
        }

        public byte PayloadType
        {
            get { return (byte)TouchServiceTcpCommunicationPayloadTypes.ShowMessage; }
        }

        public byte[] ToByteArray()
        {
            using (BinaryStreamWriter s = new BinaryStreamWriter())
            {
                s.Write((byte)DisplayMode);
                s.Write(Text);

                return s.ToArray();
            }
        }
    }

    
    public struct RendererChangeScreenMode : IBinaryTcpPayload
    {
        public ScreenMode ScreenMode;

        public RendererChangeScreenMode(byte[] data)
        {
            using (BinaryStreamReader sr = new BinaryStreamReader(data))
            {
                ScreenMode = (ScreenMode)sr.ReadByte();
            }
        }

        public RendererChangeScreenMode(ScreenMode screenMode)
        {
            ScreenMode = screenMode;
        }

        public byte PayloadType
        {
            get { return (byte)TouchServiceTcpCommunicationPayloadTypes.ChangeScreenMode; }
        }

        public byte[] ToByteArray()
        {
            using (BinaryStreamWriter s = new BinaryStreamWriter())
            {
                s.Write((byte)ScreenMode);

                return s.ToArray();
            }
        }
    }

    public struct RequestRedrawPayload : IBinaryTcpPayload
    {
        public Rectangle Bounds;

        public RequestRedrawPayload(byte[] data)
        {
            using (BinaryStreamReader sr = new BinaryStreamReader(data))
            {
                Bounds = sr.ReadRectangle();
            }
        }

        public RequestRedrawPayload(Rectangle bounds)
        {
            Bounds = bounds;
        }

        public byte PayloadType
        {
            get { return (byte)TouchServiceTcpCommunicationPayloadTypes.RequestRedraw; }
        }

        public byte[] ToByteArray()
        {
            using (BinaryStreamWriter s = new BinaryStreamWriter())
            {
                s.Write(Bounds);

                return s.ToArray();
            }
        }
    }

    public struct RequestSnapshotPayload : IBinaryTcpPayload
    {
        public Rectangle Bounds;

        public RequestSnapshotPayload(byte[] data)
        {
            using (BinaryStreamReader sr = new BinaryStreamReader(data))
            {
                Bounds = sr.ReadRectangle();
            }
        }

        public RequestSnapshotPayload(Rectangle bounds)
        {
            Bounds = bounds;
        }

        public byte PayloadType
        {
            get { return (byte)TouchServiceTcpCommunicationPayloadTypes.RequestScreenSnapshot; }
        }

        public byte[] ToByteArray()
        {
            using (BinaryStreamWriter s = new BinaryStreamWriter())
            {
                s.Write(Bounds);

                return s.ToArray();
            }
        }
    }



    public struct TouchEventPayload : IBinaryTcpPayload
    {
        public TouchEventType EventType;
        public Point Location;

        public TouchEventPayload(byte[] data)
        {
            using (BinaryStreamReader sr = new BinaryStreamReader(data))
            {
                EventType = (TouchEventType)sr.ReadByte();
                Location = sr.ReadPoint();
            }
        }

        public TouchEventPayload(TouchEventType eventType, Point location)
        {
            EventType = eventType;
            Location = location;
        }

        public byte PayloadType
        {
            get { return (byte)TouchServiceTcpCommunicationPayloadTypes.TouchEvent; }
        }

        public byte[] ToByteArray()
        {
            using (BinaryStreamWriter s = new BinaryStreamWriter())
            {
                s.Write((byte)EventType);
                s.Write(Location);

                return s.ToArray();
            }
        }
    }

    public struct LocationChangeEventPayload : IBinaryTcpPayload
    {
        public double Latitude;
        public double Longitude;
        public double Altitude;
        public double Course;
        public double SpeedMetersPerSecond;

        public LocationChangeEventPayload(byte[] data)
        {
            using (BinaryStreamReader sr = new BinaryStreamReader(data))
            {
                Latitude = sr.ReadDouble();
                Longitude = sr.ReadDouble();
                Altitude = sr.ReadDouble();
                Course = sr.ReadDouble();
                SpeedMetersPerSecond = sr.ReadDouble();
            }
        }

        public LocationChangeEventPayload(double latitude, double longitude, double altitude, double course, double speedMetersPerSecond)
        {
            Latitude = latitude;
            Longitude = longitude;
            Altitude = altitude;
            Course = course;
            SpeedMetersPerSecond = speedMetersPerSecond;
        }

        public byte PayloadType
        {
            get { return (byte)TouchServiceTcpCommunicationPayloadTypes.LocationChangeEvent; }
        }

        public byte[] ToByteArray()
        {
            using (BinaryStreamWriter s = new BinaryStreamWriter())
            {
                s.Write(Latitude);
                s.Write(Longitude);
                s.Write(Altitude);
                s.Write(Course);
                s.Write(SpeedMetersPerSecond);

                return s.ToArray();
            }
        }
    }







    public struct RendererStartDrawing : IBinaryTcpPayload
    {
        public Rectangle Bounds;
        public int XRounding;
        public int YRounding;

        public RendererStartDrawing(byte[] data)
        {
            using (BinaryStreamReader sr = new BinaryStreamReader(data))
            {
                Bounds = sr.ReadRectangle();
                XRounding = sr.ReadUInt16();
                YRounding = sr.ReadUInt16();
            }
        }

        public RendererStartDrawing(Rectangle rect, int xRounding, int yRounding)
        {
            Bounds = rect;
            XRounding = xRounding;
            YRounding = yRounding;
        }

        public byte PayloadType
        {
            get { return (byte)TouchServiceTcpCommunicationPayloadTypes.StartDrawing; }
        }

        public byte[] ToByteArray()
        {
            using (BinaryStreamWriter s = new BinaryStreamWriter())
            {
                s.Write(Bounds);
                s.Write((UInt16)XRounding);
                s.Write((UInt16)YRounding);

                return s.ToArray();
            }
        }
    }

    public struct RendererScreenChange : IBinaryTcpPayload
    {
        public Rectangle Bounds;

        public RendererScreenChange(byte[] data)
        {
            using (BinaryStreamReader sr = new BinaryStreamReader(data))
            {
                Bounds = sr.ReadRectangle();
            }
        }

        public RendererScreenChange(Rectangle rect)
        {
            Bounds = rect;
        }

        public byte PayloadType
        {
            get { return (byte)TouchServiceTcpCommunicationPayloadTypes.ScreenChange; }
        }

        public byte[] ToByteArray()
        {
            using (BinaryStreamWriter s = new BinaryStreamWriter())
            {
                s.Write(Bounds);

                return s.ToArray();
            }
        }
    }

    public struct RendererPushClippingArea : IBinaryTcpPayload
    {
        public Rectangle Bounds;
        public PushClippingAreaMode Mode;
        public int XRounding;
        public int YRounding;


        public RendererPushClippingArea(byte[] data)
        {
            using (BinaryStreamReader sr = new BinaryStreamReader(data))
            {
                Bounds = sr.ReadRectangle();
                Mode = (PushClippingAreaMode)sr.ReadByte();
                XRounding = sr.ReadUInt16();
                YRounding = sr.ReadUInt16();
            }
        }

        public RendererPushClippingArea(Rectangle bounds, PushClippingAreaMode mode, int xRounding, int yRounding)
        {
            Bounds = bounds;
            Mode = mode;
            XRounding = xRounding;
            YRounding = yRounding;
        }

        public byte PayloadType
        {
            get { return (byte)TouchServiceTcpCommunicationPayloadTypes.PushClippingArea; }
        }

        public byte[] ToByteArray()
        {
            using (BinaryStreamWriter s = new BinaryStreamWriter())
            {
                s.Write(Bounds);
                s.Write((byte)Mode);
                s.Write((UInt16)XRounding);
                s.Write((UInt16)YRounding);

                return s.ToArray();
            }
        }
    }



    public struct RendererDrawLine : IBinaryTcpPayload
    {
        public Point Pt1;
        public Point Pt2;
        public Color Color;

        public RendererDrawLine(byte[] data)
        {
            using (BinaryStreamReader sr = new BinaryStreamReader(data))
            {
                Pt1 = sr.ReadPoint();
                Pt2 = sr.ReadPoint();
                Color = sr.ReadColor();
            }
        }

        public RendererDrawLine(Point pt1, Point pt2, Color c)
        {
            Pt1 = pt1;
            Pt2 = pt2;
            Color = c;
        }

        public byte PayloadType
        {
            get { return (byte)TouchServiceTcpCommunicationPayloadTypes.DrawLine; }
        }

        public byte[] ToByteArray()
        {
            using (BinaryStreamWriter s = new BinaryStreamWriter())
            {
                s.Write(Pt1);
                s.Write(Pt2);
                s.Write(Color);

                return s.ToArray();
            }
        }
    }

    public struct RendererFillRectangle : IBinaryTcpPayload
    {
        public Rectangle Bounds;
        public Color Color;

        public RendererFillRectangle(byte[] data)
        {
            using (BinaryStreamReader sr = new BinaryStreamReader(data))
            {
                Bounds = sr.ReadRectangle();
                Color = sr.ReadColor();
            }
        }

        public RendererFillRectangle(Rectangle rect, Color c)
        {
            Bounds = rect;
            Color = c;
        }

        public byte PayloadType
        {
            get { return (byte)TouchServiceTcpCommunicationPayloadTypes.FillRectangle; }
        }

        public byte[] ToByteArray()
        {
            using (BinaryStreamWriter s = new BinaryStreamWriter())
            {
                s.Write(Bounds);
                s.Write(Color);

                return s.ToArray();
            }
        }
    }

    public struct RendererFillLinearGradientRectangle : IBinaryTcpPayload
    {
        public Rectangle Bounds;
        public Color Color1;
        public Color Color2;
        public float Angle;

        public RendererFillLinearGradientRectangle(byte[] data)
        {
            using (BinaryStreamReader sr = new BinaryStreamReader(data))
            {
                Bounds = sr.ReadRectangle();
                Color1 = sr.ReadColor();
                Color2 = sr.ReadColor();
                Angle = sr.ReadFloat();
            }
        }

        public RendererFillLinearGradientRectangle(Rectangle bounds, Color c1, Color c2, float angle)
        {
            Bounds = bounds;
            Color1 = c1;
            Color2 = c2;
            Angle = angle;
        }

        public byte PayloadType
        {
            get { return (byte)TouchServiceTcpCommunicationPayloadTypes.FillLinearGradientRectangle; }
        }

        public byte[] ToByteArray()
        {
            using (BinaryStreamWriter s = new BinaryStreamWriter())
            {
                s.Write(Bounds);
                s.Write(Color1);
                s.Write(Color1);
                s.Write(Angle);

                return s.ToArray();
            }
        }
    }

    public struct RendererDrawText : IBinaryTcpPayload
    {
        public Rectangle Bounds;
        public Color Color;
        public Font Font;

        public StringAlignment HorizontalAlign;
        public StringAlignment VerticalAlign;
        public StringTrimming Trimming;
        public StringFormatFlags FormatFlags;

        public String Text;

        public RendererDrawText(byte[] data)
        {
            using (BinaryStreamReader sr = new BinaryStreamReader(data))
            {
                Bounds = sr.ReadRectangle();
                Color = sr.ReadColor();
                Font = sr.ReadFont();

                HorizontalAlign = (StringAlignment)sr.ReadByte();
                VerticalAlign = (StringAlignment)sr.ReadByte();
                Trimming = (StringTrimming)sr.ReadByte();
                FormatFlags = (StringFormatFlags)sr.ReadUInt16();

                Text = sr.ReadString();
            }
        }

        public RendererDrawText(Rectangle bounds, Font font, Color c, StringAlignment horizontalAlign, StringAlignment verticalAlign, StringTrimming trimming, StringFormatFlags formatFlags, string text)
        {
            Bounds = bounds;
            Font = font;
            Color = c;

            HorizontalAlign = horizontalAlign;
            VerticalAlign = verticalAlign;
            Trimming = trimming;
            FormatFlags = formatFlags;

            Text = text;
        }

        public byte PayloadType
        {
            get { return (byte)TouchServiceTcpCommunicationPayloadTypes.DrawText; }
        }

        public byte[] ToByteArray()
        {
            using (BinaryStreamWriter s = new BinaryStreamWriter())
            {
                s.Write(Bounds);
                s.Write(Color);
                s.Write(Font);

                s.Write((Byte)HorizontalAlign);
                s.Write((Byte)VerticalAlign);
                s.Write((Byte)Trimming);
                s.Write((UInt16)FormatFlags);

                s.Write(Text);

                return s.ToArray();
            }
        }
    }

    public struct RendererDrawBorder : IBinaryTcpPayload
    {
        public Rectangle Bounds;
        public Color Color;
        public int Width;
        public ButtonBorderStyle Style;
        public int CornerRadius; // 0=square

        public RendererDrawBorder(byte[] data)
        {
            using (BinaryStreamReader sr = new BinaryStreamReader(data))
            {
                Bounds = sr.ReadRectangle();
                Color = sr.ReadColor();
                Width = (int)sr.ReadUInt16();
                Style = (ButtonBorderStyle)sr.ReadByte();
                CornerRadius = (int)sr.ReadUInt16();
            }
        }

        public RendererDrawBorder(Rectangle bounds, Color borderColor, int borderWidth, ButtonBorderStyle borderStyle, int cornerRadius)
        {
            Bounds = bounds;
            Color = borderColor;
            Width = borderWidth;
            Style = borderStyle;
            CornerRadius = cornerRadius;
        }

        public byte PayloadType
        {
            get { return (byte)TouchServiceTcpCommunicationPayloadTypes.DrawBorder; }
        }

        public byte[] ToByteArray()
        {
            using (BinaryStreamWriter s = new BinaryStreamWriter())
            {
                s.Write(Bounds);
                s.Write(Color);
                s.Write((UInt16)Width);
                s.Write((byte)Style);
                s.Write((UInt16)CornerRadius);

                return s.ToArray();
            }
        }
    }


    public struct RendererDrawNamedImage : IBinaryTcpPayload
    {
        public Rectangle Bounds;
        public float Opacity; // send 0-255 (but our variable really holds 0.0-1.0).
        public ImageSizeMode SizeMode;
        public string ImagePath;
        public Int32 ImageHash;

        public RendererDrawNamedImage(byte[] data)
        {
            using (BinaryStreamReader sr = new BinaryStreamReader(data))
            {
                Bounds = sr.ReadRectangle();
                Opacity = (float)(sr.ReadByte() / 255.0);
                SizeMode = (ImageSizeMode)sr.ReadByte();
                ImagePath = sr.ReadString();
                ImageHash = sr.ReadInt32();
            }
        }

        public RendererDrawNamedImage(Rectangle bounds, float opacity, ImageSizeMode sizeMode, string imagePath, Int32 imageHash)
        {
            Bounds = bounds;
            Opacity = opacity;
            SizeMode = sizeMode;
            ImagePath = imagePath;
            ImageHash = imageHash;
        }

        public byte PayloadType
        {
            get { return (byte)TouchServiceTcpCommunicationPayloadTypes.DrawNamedImage; }
        }

        public byte[] ToByteArray()
        {
            using (BinaryStreamWriter s = new BinaryStreamWriter())
            {
                s.Write(Bounds);
                s.Write((byte)(Opacity * 255)); // send 0-255 (but our variable really holds 0.0-1.0).
                s.Write((byte)SizeMode);
                s.Write(ImagePath);
                s.Write((Int32)ImageHash);
                return s.ToArray();
            }
        }
    }

    public struct RendererDrawImage : IBinaryTcpPayload, IDisposable
    {
        public Rectangle Bounds;
        public float Opacity; // send 0-255 (but our variable really holds 0.0-1.0).
        public ImageSizeMode SizeMode;
        public Image Image;

        private MemoryStreamImage _msImage;

        public RendererDrawImage(byte[] data)
        {
            using (BinaryStreamReader sr = new BinaryStreamReader(data))
            {
                Bounds = sr.ReadRectangle();
                Opacity = (float)(sr.ReadByte() / 255.0);
                SizeMode = (ImageSizeMode)sr.ReadByte();
                _msImage = sr.ReadImage();
                Image = _msImage.Image;
            }
        }

        public RendererDrawImage(Rectangle bounds, float opacity, ImageSizeMode sizeMode, Image img)
        {
            Bounds = bounds;
            Opacity = opacity;
            SizeMode = sizeMode;
            Image = img;
            _msImage = null;
        }

        public byte PayloadType
        {
            get { return (byte)TouchServiceTcpCommunicationPayloadTypes.DrawImage; }
        }

        public byte[] ToByteArray()
        {
            using (BinaryStreamWriter s = new BinaryStreamWriter())
            {
                s.Write(Bounds);
                s.Write((byte)(Opacity * 255)); // send 0-255 (but our variable really holds 0.0-1.0).
                s.Write((byte)SizeMode);
                s.Write(Image);
                return s.ToArray();
            }
        }

        public void Dispose()
        {
            Image = null;

            if (_msImage != null)
            {
                _msImage.Dispose();
                _msImage = null;
            }
        }
    }

    public struct RendererNamedImage : IBinaryTcpPayload
    {
        public string ImagePath;
        public Int32 ImageHash;
        public NamedImageFormat ImageFormat;
        public byte[] ImageBytes;

        public RendererNamedImage(byte[] data)
        {
            using (BinaryStreamReader sr = new BinaryStreamReader(data))
            {
                ImagePath = sr.ReadString();
                ImageHash = sr.ReadInt32();
                ImageFormat = (NamedImageFormat)sr.ReadByte();
                ImageBytes = sr.ReadByteArrayWithLength();
            }
        }

        public RendererNamedImage(string imagePath, int imageHash, ImageFormat imgFormat, byte[] imgBytes)
        {
            ImagePath = imagePath;
            ImageHash = imageHash;
            ImageFormat = RendererNamedImage.ImageFormatToNamedImageFormat(imgFormat);
            ImageBytes = imgBytes;
        }

        public byte PayloadType
        {
            get { return (byte)TouchServiceTcpCommunicationPayloadTypes.NamedImage; }
        }

        public byte[] ToByteArray()
        {
            using (BinaryStreamWriter s = new BinaryStreamWriter())
            {
                s.Write(ImagePath);
                s.Write((Int32)ImageHash);
                s.Write((byte)ImageFormat);
                s.WriteByteArrayWithLength(ImageBytes);

                return s.ToArray();
            }
        }

        //public MemoryStreamImage MemoryStreamImage
        //{
        //    get
        //    {
        //        return new MemoryStreamImage(ImageBytes);
        //    }
        //}

        public static ImageFormat NamedImageFormatToImageFormat(NamedImageFormat imgFormat)
        {
            if (imgFormat == NamedImageFormat.Png)
                return System.Drawing.Imaging.ImageFormat.Png;
            else if (imgFormat == NamedImageFormat.Gif)
                return System.Drawing.Imaging.ImageFormat.Gif;
            else if (imgFormat == NamedImageFormat.Jpeg)
                return System.Drawing.Imaging.ImageFormat.Jpeg;
            else if (imgFormat == NamedImageFormat.Bmp)
                return System.Drawing.Imaging.ImageFormat.Bmp;
            // The following are not supported on PocketPC and likely other devices.
            //else if (imgFormat == NamedImageFormat.Emf)
            //    return System.Drawing.Imaging.ImageFormat.Emf;
            //else if (imgFormat == NamedImageFormat.Exif)
            //    return System.Drawing.Imaging.ImageFormat.Exif;
            //else if (imgFormat == NamedImageFormat.Icon)
            //    return System.Drawing.Imaging.ImageFormat.Icon;
            //else if (imgFormat == NamedImageFormat.Tiff)
            //    return System.Drawing.Imaging.ImageFormat.Tiff;
            //else if (imgFormat == NamedImageFormat.Wmf)
            //    return System.Drawing.Imaging.ImageFormat.Wmf;
            else
                throw new Exception("NamedImageFormatToImageFormat: Unsupported image format: " + ((int)imgFormat));
        }

        public static NamedImageFormat ImageFormatToNamedImageFormat(ImageFormat imgFormat)
        {
            if (imgFormat.Equals(System.Drawing.Imaging.ImageFormat.Png))
                return NamedImageFormat.Png;
            else if (imgFormat.Equals(System.Drawing.Imaging.ImageFormat.Gif))
                return NamedImageFormat.Gif;
            else if (imgFormat.Equals(System.Drawing.Imaging.ImageFormat.Jpeg))
                return NamedImageFormat.Jpeg;
            else if (imgFormat.Equals(System.Drawing.Imaging.ImageFormat.Bmp))
                return NamedImageFormat.Bmp;
            // The following are not supported on PocketPC and likely other devices.
            //else if (imgFormat.Equals(System.Drawing.Imaging.ImageFormat.Emf))
            //    return NamedImageFormat.Emf;
            //else if (imgFormat.Equals(System.Drawing.Imaging.ImageFormat.Exif))
            //    return NamedImageFormat.Exif;
            //else if (imgFormat.Equals(System.Drawing.Imaging.ImageFormat.Icon))
            //    return NamedImageFormat.Icon;
            //else if (imgFormat.Equals(System.Drawing.Imaging.ImageFormat.Tiff))
            //    return NamedImageFormat.Tiff;
            //else if (imgFormat.Equals(System.Drawing.Imaging.ImageFormat.Wmf))
            //    return NamedImageFormat.Wmf;
            else
                throw new Exception("ImageFormatToNamedImageFormat: Unsupported image format: " + imgFormat.ToString());
        }
    }

    public struct RequestNamedImagePayload : IBinaryTcpPayload
    {
        public string ImageName;

        public RequestNamedImagePayload(byte[] data)
        {
            using (BinaryStreamReader sr = new BinaryStreamReader(data))
            {
                ImageName = sr.ReadString();
            }
        }

        public RequestNamedImagePayload(string imageName)
        {
            this.ImageName = imageName;
        }

        public byte PayloadType
        {
            get { return (byte)TouchServiceTcpCommunicationPayloadTypes.RequestNamedImage; }
        }

        public byte[] ToByteArray()
        {
            using (BinaryStreamWriter s = new BinaryStreamWriter())
            {
                s.Write(ImageName);

                return s.ToArray();
            }
        }
    }
}
